package com.assembler.app.debruij.succinct;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.assembler.app.debruij.model.CompositeItem;
import com.assembler.app.debruij.model.Edge;
import com.assembler.app.debruij.model.Node;

/**
 * This class is implementation of Builder pattern. It is responsible for
 * building graph using chain of steps.
 * 
 * This class is a part of the lab rotation project for PhD  students in January 2014.
 * School of Computing
 * National University Singapore.
 * 
 * @author Iana Pyrogova, Abha Belorkar
 * @version 1.0.1
 */

public class GraphBuilder {

	private DeBruijnGraph graph;

	synchronized public DeBruijnGraph createNewGraph(String sequence, int kMerLength) {
		graph = new DeBruijnGraph(sequence, kMerLength);
		initGraph();
		return graph;
	}

	private void initGraph() {
		initComposites();
		sortNodes();
		labeledIdenticalIncomingEdges();
		initLFieldInComposites();
		initFMap();
	}

	/**
	 * Initialize unsorted array of nodes and a corresponding array w.
	 */
	protected void initComposites() {
		List<CompositeItem> composites = new ArrayList<CompositeItem>();
		int kMerLength = graph.getkMerLength();
		String sequence = graph.getSequence();
		for (int i = 0; i < sequence.length() - kMerLength; i++) {
			Node node = new Node(sequence.substring(i, i + kMerLength));
			Edge edge = new Edge(Character.toString(sequence.charAt(i + kMerLength)));
			CompositeItem compositeItem = new CompositeItem(node, edge, null);
			if (!composites.contains(compositeItem)) {
				composites.add(compositeItem);
			}
		}
		graph.setComposites(composites);
	}

	/**
	 * sort nodes and rearrange w accordingly.
	 */
	protected void sortNodes() {
		Collections.sort(graph.getComposites(), new NodeComparator());
	}

	protected void labeledIdenticalIncomingEdges() {
		List<CompositeItem> composites = graph.getComposites();
		for (int i = 0; i < composites.size(); i++) {
			List<CompositeItem> similarItems = getNodeListBySufixFromPos(composites.get(i)
					.getNode().getSufix(), i + 1);
			CompositeItem currentItem = composites.get(i);

			for (CompositeItem item : similarItems) {
				if (!item.equals(currentItem)
						&& item.getEdge().getEdge().equals(currentItem.getEdge().getEdge())) {
					item.getEdge().setLabel(false);
				}
			}
		}
	}

	protected void initLFieldInComposites() {
		List<CompositeItem> composites = graph.getComposites();
		for (int i = 0; i < composites.size() - 1; i++) {
			CompositeItem firstComposite = composites.get(i);
			CompositeItem secondComposite = composites.get(i + 1);
			if (!firstComposite.getNode().equals(secondComposite.getNode())) {
				firstComposite.setL(true);
			} else {
				firstComposite.setL(false);
			}
		}
		// set last item which is always positive
		composites.get(composites.size() - 1).setL(true);
	}

	protected void initFMap() {
		// This method could be optimized as we don't need to scan list till the
		// end as soon as we found last symbol from alphabet
		int firstPositionOfCurrent = 0;
		List<CompositeItem> composites = graph.getComposites();
		for (int i = 0; i < composites.size() - 1; i++) {
			String nodeValue1 = composites.get(i).getNode().getNode();
			char lastChar1 = nodeValue1.charAt(nodeValue1.length() - 1);
			String nodeValue2 = composites.get(i + 1).getNode().getNode();
			char lastChar2 = nodeValue2.charAt(nodeValue2.length() - 1);
			if (lastChar1 != lastChar2) {
				graph.getfMap().put(Character.toString(lastChar1), firstPositionOfCurrent);
				firstPositionOfCurrent = i + 1;
			}
		}
		// adding last element
		String lastNodeValue = composites.get(firstPositionOfCurrent).getNode().getNode();
		char lastChar = lastNodeValue.charAt(lastNodeValue.length() - 1);
		graph.getfMap().put(Character.toString(lastChar), firstPositionOfCurrent);
	}

	private List<CompositeItem> getNodeListBySufixFromPos(String suffix, int position) {
		List<CompositeItem> res = new ArrayList<CompositeItem>();
		List<CompositeItem> composites = graph.getComposites();
		for (int j = position; j < composites.size(); j++) {
			if (composites.get(j).getNode().getNode().endsWith(suffix)) {
				res.add(composites.get(j));
			}
		}
		return res;
	}

	private static class NodeComparator implements Comparator<CompositeItem> {

		@Override
		public int compare(CompositeItem o1, CompositeItem o2) {
			String item1 = new StringBuilder(o1.getNode().getNode()).reverse().toString();
			String item2 = new StringBuilder(o2.getNode().getNode()).reverse().toString();
			return item1.compareTo(item2);
		}
	}

}
